﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Collections;
using Collections.TreeCollections;
using System.Xml.Linq;
using System.IO;

namespace FeatureModelTool.ProjectView.ProjectTreeStructures
{
    public class ProjectTree : Tree
    {
        #region XML Symbol
        public readonly static string _XML_PROJECT_NAME = "ProjectName";
        #endregion

        static ProjectTree()
        {
            TreeRegisterClass treeClass = new TreeRegisterClass(typeof(ProjectTree)
    , ProjectTree.ConstructProjectTree
    , ProjectTree.ConstructProjectTreeRoot
    , ProjectTree.XmlParser);

            // project tree root
            TreeObjectRegisterClass projRoot = new TreeObjectRegisterClass(
                typeof(ProjectTreeRoot), ProjectTreeRoot.ConstructRoot, ProjectTreeRoot.xmlParser);
            treeClass.RegisterTreeObjectType(projRoot);

            // project folder
            TreeObjectRegisterClass projFolder = new TreeObjectRegisterClass(
                typeof(ProjectFolder), ProjectFolder.ConstructFolder, ProjectFolder.xmlParser);
            treeClass.RegisterTreeObjectType(projFolder);

            // project file
            TreeObjectRegisterClass projFile = new TreeObjectRegisterClass(
                typeof(ProjectFile), ProjectFile.ConstructFile, ProjectFile.xmlParser);
            treeClass.RegisterTreeObjectType(projFile);

            TreeRegisterTable.TreeClassRegisterTable.RegisterTreeClass(treeClass);
        }

        #region Properties
        /// <summary>
        /// Folder Full Path
        /// </summary>
        public string ProjectFileFullPath { get { return this.FullFilePath; } }
        public string ProjectRootPath { get { return FolderPath; } }
        public string ProjectName
        {
            get { return _projName; }
            set
            {
                this.PreProcess(this, null);
                _projName = value;
                this.ProProcess(this, null);
            }
        }
        #endregion

        #region Fields
        private string _projName;
        private readonly static string _EXTEND_NAME = "fmProj";
        #endregion

        public ProjectTree(string folderPath, string projName
            , bool create)
            : base()
        {
            string fullPath = folderPath + "\\" + projName + "\\" + projName + "." + _EXTEND_NAME;
            SetFileInfo(fullPath);

            _projName = projName;
            _dirty = false;
            if(create)
            {
                createNewProject();
            }
        }
        
        public ProjectTree(ProjectTreeRoot root, string folderPath
            , string projName, bool create)
            : base(root)
        {
            _projName = projName;
            string fullPath = folderPath + projName + "\\" + projName + "." + _EXTEND_NAME;
            SetFileInfo(fullPath);

            _projName = projName;
            _dirty = false;
            if (create)
            {
                createNewProject();
            }
        }

        #region Create New Instance
        /// <summary>
        /// Create New File
        /// </summary>
        /// <returns></returns>
        public ProjectFile CreateNewFile(INonLeaf parent, string fileName, bool create)
        {
            string parentFolderPath = string.Empty;
            if (parent is ProjectTreeRoot)
            {
                ProjectTreeRoot root = (ProjectTreeRoot)parent;
                parentFolderPath = root.FullPath;
            }
            else if (parent is ProjectFolder)
            {
                ProjectFolder folder = (ProjectFolder)parent;
                parentFolderPath = folder.FullPath;
            }

            if (parentFolderPath == string.Empty)
            {
                return null;
            }
            // valid file exists
            string filePath = parentFolderPath + "\\"+ fileName;
            if (create && File.Exists(filePath))
            {
                throw new Exception("The File already exists.");
            }

            ProjectFile file = (ProjectFile)CreateNewInstance(
                typeof(ProjectFile), parent, fileName, create);
            // create file
            if (create)
            {
                File.CreateText(filePath);
            }

            return file;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="folderName"></param>
        /// <param name="create"></param>
        /// <returns></returns>
        public ProjectFolder CreateNewFolder(INonLeaf parent, string folderName, bool create)
        {
            string parentFolderPath = string.Empty;
            if (parent is ProjectTreeRoot)
            {
                ProjectTreeRoot root = (ProjectTreeRoot)parent;
                parentFolderPath = root.FullPath;
            }
            else if (parent is ProjectFolder)
            {
                ProjectFolder folder = (ProjectFolder)parent;
                parentFolderPath = folder.FullPath;
            }

            if (parentFolderPath == string.Empty)
            {
                return null;
            }
            // valid file exists
            string folderPath = parentFolderPath + folderName;
            if (create && Directory.Exists(folderPath))
            {
                throw new Exception("The Folder already exists.");
            }

            ProjectFolder newfolder = (ProjectFolder)CreateNewInstance(
                typeof(ProjectFolder), parent, folderName, create);
            // create file
            if (create)
            {
                Directory.CreateDirectory(folderPath);
            }

            return newfolder;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="fileFullPath"></param>
        /// <returns></returns>
        public ProjectFile AddExistFile(INonLeaf parent, string orgfileFullPath)
        {
            FileInfo fileInfo = new FileInfo(orgfileFullPath);
            if (fileInfo == null)
            {
                throw new Exception(string.Format("No Such File: {0}", orgfileFullPath));
            }
            string fileName = fileInfo.Name;

            #region calculate destination file full path
            string parentFolderPath = string.Empty;
            if (parent is ProjectTreeRoot)
            {
                ProjectTreeRoot root = (ProjectTreeRoot)parent;
                parentFolderPath = root.FullPath;
            }
            else if (parent is ProjectFolder)
            {
                ProjectFolder folder = (ProjectFolder)parent;
                parentFolderPath = folder.FullPath;
            }

            if (parentFolderPath == string.Empty)
            {
                return null;
            }
            #endregion
            string dstfilePath = parentFolderPath + "\\" + fileName;
            bool needCreateInstance = true; // need to create instance in project tree
            bool needCreateFileSystem = true; // need to create file in file system
            #region check file name
            if (dstfilePath.Equals(orgfileFullPath))
            {// dst is same with org file
                needCreateFileSystem = false;
                if (parent is ProjectFolder)
                {
                    ProjectFolder folder = (ProjectFolder)parent;
                    if (folder.ContainFile(fileName))
                    {
                        needCreateInstance = false;
                    }
                }
                else if (parent is ProjectTreeRoot)
                {
                    ProjectTreeRoot root = (ProjectTreeRoot)parent;
                    if (root.ContainFile(fileName))
                    {
                        needCreateInstance = false;
                    }
                }
            }
            else
            {
                if (File.Exists(dstfilePath))
                {
                    throw new Exception(string.Format("File already exists: {0}", fileName));
                }
            }
            #endregion

            ProjectFile file = null;
            if (needCreateInstance)
            {
                file = (ProjectFile)CreateNewInstance(
                    typeof(ProjectFile), parent, fileName, true);
            }
            // create file
            if (needCreateFileSystem)
            {
                using (StreamReader reader = File.OpenText(orgfileFullPath))
                {
                    string strReader = reader.ReadToEnd();
                    using (StreamWriter writer = File.CreateText(dstfilePath))
                    {
                        writer.Write(strReader);
                    }
                }
            }

            return file;
        }
        #endregion

        #region Static Constructor
        /// <summary>
        /// args[0]: folderPath
        /// args[1]: poject name
        /// </summary>
        /// <returns></returns>
        public static ProjectTree ConstructProjectTree(object[] args)
        {
            string folderPath = string.Empty;
            string projectName = string.Empty;
            if (args != null)
            {
                folderPath = args[0].ToString();
                projectName = args[1].ToString();
            }

            return new ProjectTree(folderPath, projectName, false);
        }
        /// <summary>
        /// args[0]: folderpath
        /// args[1]: project name
        /// </summary>
        /// <param name="root"></param>
        /// <returns></returns>
        public static ProjectTree ConstructProjectTreeRoot(TreeRoot root, object[] args)
        {
            string folderPath = string.Empty;
            string projectName = string.Empty;
            if (args != null)
            {
                folderPath = args[0].ToString();
                projectName = args[1].ToString();
            }

            ProjectTreeRoot projRoot = root as ProjectTreeRoot;
            return new ProjectTree(projRoot, folderPath, projectName, false);
        }
        #endregion

        #region Xml Parser
        /// <summary>
        /// To Xelem
        /// </summary>
        /// <returns></returns>
        public override XElement ToXElem()
        {
            XElement xelem = base.ToXElem();
            xelem.Add(new XAttribute(_XML_PROJECT_NAME, _projName));
            return xelem;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="xelem"></param>
        /// <param name="register"></param>
        /// <returns></returns>
        public static void XmlParser(XElement xelem, Tree tree)
        {
            ProjectTree projTree = tree as ProjectTree;
            projTree._projName = xelem.Attribute(_XML_PROJECT_NAME).Value;
        }
        #endregion

        public override object Clone()
        {
            XElement xelem = this.ToXElem();
            ProjectTree projTree = (ProjectTree)Tree.XElemParser(xelem);

            projTree._folderPath = this._folderPath;

            return projTree;
        }
        /// <summary>
        /// Create new project
        /// </summary>
        private void createNewProject()
        {
            // valid
            if (ProjectRootPath == null || ProjectRootPath == string.Empty
                || _projName == null || _projName == string.Empty)
            {
                return;
            }
            if (!Directory.Exists(this.ProjectRootPath))
            {
                Directory.CreateDirectory(ProjectRootPath);
            }
            // save proj in the directory
            XDocument xdoc = this.ToXDoc();
            
            using (StreamWriter writer = File.CreateText(this.FullFilePath))
            {
                writer.Write(xdoc.ToString());
            }
        }

        #region Save & Load
        /// <summary>
        /// Save current project
        /// </summary>
        public void Save()
        {
            if (this._dirty == false)
            {
                return;
            }
            // save project file
            createNewProject();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="projFilePath"></param>
        /// <returns></returns>
        public static ProjectTree Load(string projFilePath)
        {
            string fileStr = string.Empty;
            using (StreamReader reader = File.OpenText(projFilePath))
            {
                fileStr = reader.ReadToEnd();
            }
            XDocument xdoc = XDocument.Parse(fileStr);
            ProjectTree projTree = (ProjectTree)Tree.XDocParser(typeof(ProjectTree), xdoc);

            projTree.SetFileInfo(projFilePath);

            projTree._dirty = false; // just for load
            return projTree;
        }
        #endregion
    }
}
